<?php
class db{
	// This class wont be instantiated
	private function db(){
	}

    public static function createAllTables($delete = true){
        echo "<h2>Generating Tables</h2>";

        foreach(ReflectionUtils::getSubClasses("Persistable") as $class){
            echo "<p>Generating table for class: $class</p>";
            db::createTables($class, true);
        }
    }

	public static function createTables($className, $delete = false){
		$obj = new $className();

		// If object doesnt extend Persistable, dont do nothing
		if(!($obj instanceof Persistable)){
			return;
		}

		$parentClass = get_parent_class($obj);

		if($parentClass == "Persistable") {
			// It does directly implement Persistable, create table for this object
			db::createNewTable($className, $delete);
		} else {
			// First of all, tell all the parent to make tables
			db::createTables($parentClass);

			// Now agregate all the fields that are necessary
			db::modifyTable($className, $parentClass);
		}
	}

	protected static function getTableName($obj) {
		$parentClass = get_parent_class($obj);

		if ($parentClass != "Persistable") {
			$parent_obj = new $parentClass();
			return db::getTableName($parent_obj);
		} else {
			if(isset($obj->mapping["tablename"])){
				return $obj->mapping["tablename"];
			} else {
				return get_class($obj);
			}
		}
	}

	/**
	 * This method creates a new Table in the db
	 * and deletes the old one if there was one
	 */
	protected static function createNewTable($objClass, $delete = false) {
		// Get the current table name
		$obj = new $objClass();
		$tablename = db::getTableName($obj);
		$database = new dbConnection();

		// Delete all table
		if($delete){
			$query = "DROP TABLE IF EXISTS `".$tablename."` ;";
			$database->query($query);
		}

		$query = "CREATE TABLE IF NOT EXISTS `" . $tablename . "` (`id` INT AUTO_INCREMENT, `discriminator` VARCHAR(255) ";

		foreach ($obj->mapping as $key => $value) {
			$type = $value["type"];
			if($type == "LIST"){
				// Dont do Anything
				// List will be saved into a separated table
				continue;
			}
			if($type == "OBJECT"){
				$fieldstr = "`$key` INT";
			} else if($type == "VARCHAR"){
				$fieldstr = "`$key` ".$value["type"]." (".$value["size"].")";
			} else if($type != "LIST"){
				$fieldstr = "`$key` ".$value["type"];
			}
			$query .= ", ".$fieldstr.((isset($value["unique"]) && $value["unique"] == "TRUE") ? " UNIQUE" : "");
		}
		$query .= ", PRIMARY KEY (`id`));";

		echo "<code> ".$query."</code><br>";
		$database->query($query);
	}

	protected static function modifyTable($objClass, $parentClass){
		$obj = new $objClass();

		$parent = new $parentClass();
		$tablename = db::getTableName($parent);

		$database = new dbConnection();

		// First of all, analyse wich variables do we really need to add
		$variables = array();
		$tempObj = $obj;
		while(($parent = get_parent_class($tempObj)) != "Persistable"){
			$tempObj = new $parent();
			$variables = array_merge($variables, $tempObj->mapping);
		}

		echo "<b>query for ".get_class($objClass).":</b><code> ";
		foreach ($obj->mapping as $key => $value) {
			$type = $value["type"];
			if(isset($variables[$key]) || $type == "LIST"){
				continue;
			} else if($type == "OBJECT"){
				$fieldstr = "`$key` INT";
			} else if($type == "VARCHAR"){
				$fieldstr = "`$key` ".$value["type"]." (".$value["size"].")";
			} else {
				$fieldstr = "`$key` ".$value["type"];
			}
			$query = "ALTER TABLE `".$tablename."` ADD ".$fieldstr.((isset($value["unique"]) && $value["unique"] == "TRUE") ? " UNIQUE" : "").";";
			try{
				$database->query($query);
			} catch (Exception $e) {
			}

			echo $query."<br>";
		}
		echo "</code>";
	}

	/**
	* Gets an object from database
	* @param integer $id
	* @param classname $className
	*/
	public static function get($className, $id = null) {
		if(!isset($className) || $className == ""){
			return null;
		}

		if(!isset($id)){
			$obj = new $className();

			$query = array();
			$query[] = array("id", ">=", "0");

			while(($parent = get_parent_class($obj)) != "Persistable"){
				$obj = new $parent();
				$query[] = array("discriminator", "!=", $parent);
			}
			$list = db::getList($className, $query);
			return $list;
		}

		if($id == 0){
			return null;
		}

		$tablename = db::getTableName(new $className());
		$database = new dbConnection();

		$query = "select * from `" . $tablename . "` where `id`='" . intval($id) . "' LIMIT 1";
		$database->query($query);

		if($database->rows() == null){
			return null;
		}
		$className = $database->result(0, "discriminator");

		$obj = new $className();
		$obj->setId($database->result(0, "id"));

		foreach ($obj->getMapping() as $key => $value) {
			$setkey = "set".$key;
			$getkey = "get".$key;

			if ($value["type"] == "OBJECT") {
				$objid = $key."_id";
				$obj->$objid = $database->Result(0, $key);
			} else
				if ($value["type"] == "LIST") {
					// LOADS A LIST OF OBJECTS
					$obj->$setkey(new LinkedList($obj, $value["target"]));
					$obj->$getkey()->restore();
				} else {
					// LOADS PRIMITIVE DATA TYPEN
					$obj->$setkey($database->Result(0, $key));
				}
		}
		return $obj;
	}

	/**
	* Returns a sorted array of objects that match given conditions
	* @param multidimensional array {("field", "comparator", "value"), ("field", "comparator", "value"), ...}
	* @param string $sortBy
	* @param boolean $ascending
	* @param int limit
	*/
	public function getList($className, $fcv_array, $sortBy = "id", $order = "ASC", $limit = '') {
		$obj = new $className();
		$tablename = db::getTableName($obj);

		$sqlLimit = ($limit != '' && $sortBy == '' ? "LIMIT $limit" : '');
		if (sizeof($fcv_array) > 0) {
			$listObj = Array();
			$database = new dbConnection();
			$query = "select id from " . $tablename . " where ";

			for ($i = 0, $c = sizeof($fcv_array); $i < $c; $i++) {
				if($i > 0 && $fcv_array[$i][1] != ""){
					$query .= " AND ";
				}
				if($fcv_array[$i][0] == "QUERY"){
					$query .= $fcv_array[$i][1];
				} else {
					$query .= " `" . $fcv_array[$i][0] . "` " . $fcv_array[$i][1] . " '" . $fcv_array[$i][2] . "'";
				}
			}
			$query .= "order by `".$sortBy."` ".$order.";";
			$database->query($query);

			for ($i = 0; $i < $database->rows(); $i++) {
				$listObj[] = db::get($className, $database->Result($i, "id"));
			}

			$rlist = array();

			for($i = 0; $i < count($listObj); $i++){
				if($listObj[$i] instanceof $className){
					$rlist[] = $listObj[$i];
				}
			}

			return $rlist;
		}
		return null;
	}

	/**
	* Saves the object to the database
	* @return integer $id
	*/

	public static function save($obj) {
		$tablename = db::getTableName($obj);

		$database = new dbConnection();

		$query = "select id from `" . $tablename . "` where `id`='" . $obj->getId() . "' LIMIT 1";

		$database->query($query);
		$update = ($database->Rows() > 0);

		if ($update) {
			$query = "update `" . $tablename . "` set ";
			$index = 0;

			foreach ($obj->getMapping() as $key => $value) {
				$setkey = "set".$key;
				$getkey = "get".$key;
				if ($value["type"] == "LIST") {
					$obj->$getkey()->save();
				} else {
					if ($index > 0) {
						$query .= ", ";
					}
					if ($value["type"] == "OBJECT") {
						$getkeyid = $key."_id";
						$query .= " `$key`= '" . $obj->$getkeyid. "'";
					} else {
						$query .= " `$key`= '" . $obj->$getkey() . "'";
					}
					$index++;
				}
			}
			$query .= " where `id`='" . $obj->getId() . "';";
		} else {
			$index = 0;
			foreach ($obj->getMapping() as $key => $value) {
				$setkey = "set".$key;
				$getkey = "get".$key;

				if ($value["type"] == "LIST") {
					if($obj->$getkey() != null){
						$obj->$getkey()->save();
					}
				} else {
					if ($index > 0) {
						$names .= ", ";
						$values .= ", ";
					}
					$names .= "`$key` ";
					if ($value["type"] == "OBJECT") {
						$getkeyid = $key."_id";
						$values .= "'" . $obj->$getkeyid. "'";
					} else {
						$values .= "'" . $obj->$getkey() . "' ";
					}
					$index++;
				}
			}
			$query = "insert into `" . $tablename . "` (`discriminator`, " . $names . ") values ('" . get_class($obj) . "', " . $values . ")";
		}
		$database->insertOrUpdate($query);

		if ($obj->getId() == "" || $obj->getId() == "0") {
			$obj->setId($database->getCurrentId());
		}
		return $obj->getId();
	}

	function getNextId($className){
		$obj = new $className();
		$tablename = db::getTableName($obj);

		$database = new dbConnection();

		$next_increment = 0;
		$qShowStatus = "SHOW TABLE STATUS LIKE '$tablename'";
		$database->query($qShowStatus);
		$qShowStatusResult = $database->result;

		while ($row = mysql_fetch_assoc($qShowStatusResult)) {
			$next_increment = $row['Auto_increment'];
		}
		mysql_free_result($qShowStatusResult);

		return $next_increment;
	}

	/**
	* Clones the object and saves it to the database
	* @return integer $id
	*/
	public static function saveNew($obj) {
		$obj->setId('');
		return db::save($obj);
	}

	/**
	* Deletes the object from the database
	* @return boolean
	*/
	public static function delete($obj, $id = null) {
		if(isset($id)){
			$obj = new $obj();
			$tablename = db::getTableName($obj);
		} else {
			$tablename = db::getTableName($obj);
			$id = $obj->getId();
		}
		$obj = db::get($obj, $id);
		$obj->Delete();
		$database = new dbConnection();
		$query = "delete from `" . $tablename . "` where `id`='" . $id . "'";
		return $database->query($query);
	}
}
?>
